package quorum;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Vector;
import java.util.Queue;
import java.util.PriorityQueue;
import common.*;
import broadcast.Broadcast;
import broadcast.BroadcastListener;
import utils.Hashes;
import test.listView;

public class Quorum extends Entity{ 
	public static final int SIBLING_LISTEN = 10003;
	public static final int SIBLING_SEND = 10004;
	public static final int SIBLING_REPLY = 10005;
	
	public static final int GROUP_LISTEN = 10013;
	public static final int GROUP_SEND = 10014;
	public static final int GROUP_REPLY = 10015;
	
	public static final int GLOBAL_LISTEN = 10023;
	public static final int GLOBAL_SEND = 10024;
	public static final int GLOBAL_REPLY = 10025;
	
	private Broadcast sibling;
	private Broadcast group;
	private Broadcast global;
	
	private static final boolean DEBUG=false;
	private static final boolean SHOWLIST=true;
	
	private static Quorum instance;
	
	public static void create(int id, String ip){
		if(instance==null)
			instance=new Quorum(id,ip);
	}
	
	public static Quorum getInstance(){
		return instance;
	}
	
	private Quorum (int id, String ip){
		super(id,ip);
		Set<Entity> destSibling=new HashSet<Entity>();
		Set<Entity> destGroup=new HashSet<Entity>();
		Set<Entity> destGlobal=new HashSet<Entity>();
		Lists.getInstance().addLiveServer(new Entity(id,ip));
		int groupId=Hashes.getGroupIdFromServerId(id);
		if((((Lists.getInstance()).getLiveServerGroup(groupId)).peek()).getId()==id){
		
			Vector<Queue<Entity>> liveservers= (Lists.getInstance()).getLiveServerList();
			for(int i=0;i<Lists.GROUP_SIZE/* && i!=groupId*/;i++){
					destSibling.add((liveservers.get(i)).peek());
			}

		}
		sibling=new Broadcast(new QuorumListener(this),destSibling,SIBLING_LISTEN,SIBLING_SEND,SIBLING_REPLY);
		
		List<Entity> staticserver=(Lists.getInstance()).getStaticServerList();
		for(int i=0;i<staticserver.size();i++){
			Entity e=staticserver.get(i);
			destGlobal.add(e);
			if(groupId == Hashes.getGroupIdFromServerId(e.getId())){
				destGroup.add(e);
			}
		}
		
		group=new Broadcast(new QuorumListener(this),destGroup,GROUP_LISTEN,GROUP_SEND,GROUP_REPLY);
		global=new Broadcast(new QuorumListener(this),destGlobal,GLOBAL_LISTEN,GLOBAL_SEND,GLOBAL_REPLY);
		
		sibling.setComponentId("sibling");
		group.setComponentId("group");
		global.setComponentId("global");
		

		Thread tr=new Thread(new ImAlive(this));
		tr.start();
		checkLive.create(this);
		
//		SyncServerThread sst=new SyncServerThread();
//		sst.start();
		
		NewBornListener nbl=new NewBornListener();
		nbl.start();
		
		if(SHOWLIST){
			listView lv=new listView();
			lv.start();
		}
	}
	public boolean writeSibling(QuorumTask task){
		Message msg=new Message(task.toString().getBytes());
		msg.setSource(this);
		sibling.setDestinations(getSiblingDest());
		return sibling.broadcast(msg);
	}
	
	public boolean writeChildren(QuorumTask task){
		Message msg=new Message(task.toString().getBytes());
		msg.setSource(this);
		return group.broadcast(msg);
	}
	
	/**just ignore checkpoint*/
//	
//	public boolean writeCheckpointSibling(){
//		/**
//		 * send all 4 lists to sibling
//		 * */
//		return false;
//	}
//	
//	public boolean writeCheckpointChildren(){
//		/**
//		 * send all 4 lists to children
//		 */
//		return false;
//	}
	
	/*
	public boolean onWake(){
		
	}
	*/
	
//	public boolean syncCheckpoint(/*List<Entity> entities*/){
//		/*synchronize checkpoints of 4 lists*/
//		return false;
//	}

	public boolean sendImAlive(){
		if(DEBUG){
		System.out.println("  <<-----broadcast imalive----->>");
		}
		
		Message msg=new Message(new String("IAMALIVE").getBytes());
		msg.setSource(this);
		return group.broadcast(msg);
	}
	
	public boolean sendImAliveLeader(){
		if(DEBUG){
		System.out.println("  <<-----broadcast imaliveleader----->>");
		}
		
		Message msg=new Message(new String("IAMALIVELEADER").getBytes());
		msg.setSource(this);
		sibling.setDestinations(getSiblingDest());
		return sibling.broadcast(msg);

	}
	
	public boolean globalImAliveLeader(){
		if(DEBUG){
		System.out.println("  <<-----global broadcast imaliveleader----->>");
		}
		
		Message msg=new Message(new String("IAMALIVELEADER").getBytes());
		msg.setSource(this);
		return global.broadcast(msg);		
	}
	
	public boolean sendSyncServer(){
		if(DEBUG){
			System.out.println("  <<-----sending sync servers----->>");
			}
		int groupId=Hashes.getGroupIdFromServerId(getId());
		String content="SYNCSERVER!"+Lists.getInstance().liveGroupString(groupId);
		Message msg=new Message(content.getBytes());
		msg.setSource(this);
		
		return global.broadcast(msg);
	}
	/*no need to check if it's leader*/
	
	public boolean detectDead(Entity e){
		
		QuorumTask task=new QuorumTask(QuorumTask.ACTION_DELETE,QuorumTask.TARGET_LIVE_SERVERS,e.toString());
		return(writeChildren(task) & writeSibling(task));
	}
	
	public boolean detectLeaderDead(Entity e){
		//surely not leader
		
		//!!need to get a new leader this should be automatic(delete that and )?
		//notify sibling, maybe not necessary in group
		int group1=Hashes.getGroupIdFromServerId(e.getId());
		int group2=Hashes.getGroupIdFromServerId(getId());
		if(group1!=group2)
			return false;
		Lists.getInstance().getLiveServerGroup(group1).remove(e);
		if(isLeader()){
			QuorumTask task=new QuorumTask(QuorumTask.ACTION_DELETE,QuorumTask.TARGET_LIVE_SERVERS,e.toString());
			writeSibling(task);
		}
		return false;
	}
	
	public boolean detectSiblingDead(Entity e){
		
		/*
		 * don't mind for new leader selection
		 *   - if that group has someone alive and it becomes new leader, it will send new 'IAMALIVELEADER' msg
		 *   - if that whole group dies, !!!!! just broadcast within its responsible team
		 * */
		QuorumTask task=new QuorumTask(QuorumTask.ACTION_DELETE,QuorumTask.TARGET_LIVE_SERVERS,e.toString());
		return(writeChildren(task));
	}
	
	public boolean isLeader(){
		int groupId=Hashes.getGroupIdFromServerId(getId());
		Lists l = Lists.getInstance();
		Queue<Entity> q=l.getLiveServerGroup(groupId);
		if(q == null){
			int i = 1;
			i++;
		}
		Entity e=q.peek();
		if(e == null){
			int i = 1;
			i++;
		}
//		System.out.println(e);
		return(e.getId()==getId());
	}
	
	public Set<Entity> getSiblingDest(){
		Set<Entity> dest=new HashSet<Entity>();
		for(int i=0;i<Lists.GROUP_SIZE;i++){
			Entity e=Lists.getInstance().getLiveServerGroup(i).peek();
			if(e!=null){
				//System.out.println("  <<dest>>"+e.toString());
			dest.add(e);
			}
		}
		if(DEBUG){
		System.out.print("     %% sibling destination:");
		for(Iterator<Entity> it=dest.iterator();it.hasNext();){
			System.out.print("| "+it.next());
		}
		System.out.println();
		}
		return new HashSet<Entity>(dest);
	}
	
	public Broadcast getSiblingBroadcast(){
		return sibling;
	}
	
	public Broadcast getGroupBroadcast(){
		return group;
	}
	public Broadcast getGlobalBroadcast(){
		return global;
	}
}